// DuokanDigestDlg.cpp : implementation file
//

#include "stdafx.h"
#include "DuokanDigest.h"
#include "DuokanDigestDlg.h"

#include "DlgAbout.h"

#include "util/UtilString.h"
#include "util/UtilsFile.h"
//////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

const CString c_str_dkp = _T("dkp2");
const CString c_str_txt = _T("txt");
//////////////////////////////////////////////////////////////////////////

BEGIN_MESSAGE_MAP(CDuokanDigestDlg, CDialog)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
    ON_BN_CLICKED(IDC_BTN_SAVE, &CDuokanDigestDlg::OnBtnSave)
    ON_WM_DROPFILES()
    ON_BN_CLICKED(IDC_ABOUT, &CDuokanDigestDlg::OnBtnAbout)
END_MESSAGE_MAP()

//////////////////////////////////////////////////////////////////////////

CDuokanDigestDlg::CDuokanDigestDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CDuokanDigestDlg::IDD, pParent)
    , m_strFileNameDkp(_T(""))
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CDuokanDigestDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_EDIT_FILE_NAME, m_strFileNameDkp);
    DDX_Control(pDX, IDC_LIST_DIGEST, m_list);
}



// CDuokanDigestDlg message handlers

BOOL CDuokanDigestDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here
    InitList();

	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CDuokanDigestDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CDuokanDigestDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


void CDuokanDigestDlg::OnBtnSave()
{
    CFileDialog fileDlg(FALSE, _T("save xxx.txt"), NULL, 0, _T("*.txt|*.txt||"), NULL);
    if(IDOK== fileDlg.DoModal() ){
        CString cstrFileName = fileDlg.GetPathName();
        bool ifok =SaveToFile(cstrFileName);
        if(!ifok){
            MessageBox(_T("save failed."), NULL, MB_OK);
        }
    }
}

void  CDuokanDigestDlg::InitList()
{
    m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT);
    //mList.SetEditBox(-1, E_LIST_COMMENT);  //just comment can edit

    m_list.InsertColumn(E_LIST_IDX, _T("Idx"), LVCFMT_LEFT, 50); 
    m_list.InsertColumn(E_LIST_STRING, _T("String"), LVCFMT_LEFT, 300); 
}

void  CDuokanDigestDlg::UpdateList()
{
    m_list.SetRedraw(FALSE);

    m_list.DeleteAllItems();

    CString cstr;

    const UINT dataNum = (UINT)m_vecDigest.size();
    for (UINT idx = 0; idx < dataNum; idx++)
    {
        const CDigest& entry = m_vecDigest[idx];

        cstr.Format(_T("%d"), idx);
        m_list.InsertItem(idx, cstr );
        
        m_list.SetItemText(idx, E_LIST_STRING, entry.m_str );
        //m_list.SetItemText(idx, E_LIST_VALUE, m_vecData[idx].m_strValue.c_str() );
    }

    m_list.SetRedraw(TRUE);
}

void CDuokanDigestDlg::OnDropFiles(HDROP   hDropInfo)
{
    const int c_char_size = 512;
    TCHAR*  lpszFileName = new   TCHAR[c_char_size]; 
    int   nFileCount = 0; 
    nFileCount=::DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, c_char_size); 
    //::AfxMessageBox(itoa(nFileCount,cFileCount,10)); 

    //only accept 1 file
    CString cstr;
    for   (int i=0;i <nFileCount;i++) 
    { 
        UINT   nChars=::DragQueryFile   (hDropInfo,i, &lpszFileName[0], 512); 
        cstr =  lpszFileName ; 
        break;
    }     
    SAFE_DELETE_ARRAY( lpszFileName ); 
    ::DragFinish   (hDropInfo); 

    if(cstr.IsEmpty() )
        return;

    //check file
    const CString cstrPoint = _T(".");
    int findPos = cstr.ReverseFind(_T('.'));
    if(findPos < 0 || findPos >= cstr.GetLength() )
        return ;
    CString cstrLast = cstr.Mid(findPos + 1);
    CString cstrPrefix = cstr.Mid(0, findPos+1);
    if(cstrLast == c_str_dkp){
        m_strFileNameDkp = cstr;
        m_strFileNameTxt = cstrPrefix + c_str_txt;
    }
    else if(cstrLast == c_str_txt){
        m_strFileNameDkp = cstrPrefix + c_str_dkp;
        m_strFileNameTxt = cstr;
    }
    else{
        return;
    }
    UpdateData(FALSE);  //data to control

    ParseFile();
    UpdateList();
    
    __super::OnDropFiles(hDropInfo);  
}

bool CDuokanDigestDlg::ParseFile()
{
    bool ifok = true;
    ifok = ifok && ParseFileDkp();
    ifok = ifok && ParseFileTxt();
    return ifok;
}

bool    CDuokanDigestDlg::ParseFileTxt()
{
    if(m_vecDigest.empty() )
        return false;

    CFile file(m_strFileNameTxt, CFile::modeRead);
    if(file.GetLength()< 1 )
        return false;

    int readSize = 0;
    UINT readRet = 0;
    vector<char> vecTemp;
    vecTemp.resize(20, 0);
    string strTemp;
    for(UINT digestIdx =0; digestIdx < m_vecDigest.size(); digestIdx++){
        CDigest& entry = m_vecDigest[digestIdx];
        readSize = entry.m_idxEnd - entry.m_idxStart;
        if(readSize < 1
            || readSize > 5000) //memory limit
            continue;
        vecTemp.clear();
        vecTemp.resize(readSize+1, 0);
        
        readRet = (UINT)file.Seek(entry.m_idxStart, 0);
        if(readRet != entry.m_idxStart)
            continue;

        readRet = file.Read(&(vecTemp[0]), readSize);
        if(readRet != readSize)
            continue;

        strTemp = (char*)&(vecTemp[0]);
        entry.m_str = UtilString::ConvertMultiByteToWideChar(strTemp).c_str();

    }
    file.Close();
    return true;
}

bool    CDuokanDigestDlg::ParseFileDkp()
{
    m_vecDigest.clear();

    if(!UtilFile::FileExists(m_strFileNameDkp.GetString() ) 
        || !UtilFile::FileExists(m_strFileNameTxt.GetString() )
        ){
            MessageBox(_T("Can't open file."), NULL, MB_OK);
            return false;
    }

    CFile fileDkp(m_strFileNameDkp, CFile::modeRead);
    const UINT fileSizeDkp = (UINT)fileDkp.GetLength();
    vector<byte> vecByteDkp;
    vecByteDkp.resize(fileSizeDkp);
    UINT readRet = fileDkp.Read(&vecByteDkp[0], fileSizeDkp);
    fileDkp.Close();

    const UINT c_headIdx = 0x0e28;
    const UINT c_off_digestNum = 0x09;
    const UINT c_off_digest_0 = 0x08;
    const UINT c_off_digest_addr_start = 0x10;
    const UINT c_off_digest_addr_end = 0x08;
    const UINT c_one_digest_len = 44;
  
    bool ifok = false;
    UINT curIdx = c_headIdx;
    //UINT findIdx = 0;
    UINT partIdx = 0; //05ffffff  part
    UINT partNum = 0;

    //byte digestHead[5]; int i=0;
    VEC_BYTE vecDigestHead ;  //05ffffff  part
    vecDigestHead.push_back(05);
    vecDigestHead.push_back(0xff);
    vecDigestHead.push_back(0xff);
    vecDigestHead.push_back(0xff);
    vecDigestHead.push_back(0xff);

    VEC_BYTE vecSingleDigestHead ;
    vecSingleDigestHead.push_back(02);
    vecSingleDigestHead.push_back(0);
    vecSingleDigestHead.push_back(0);
    vecSingleDigestHead.push_back(0);
    
    CDigest entry;

    while(true){
        ifok = FindVectorInVector(vecByteDkp,vecDigestHead, partIdx+vecDigestHead.size(), partIdx);
        if(ifok){
            curIdx = partIdx;
            partNum++;
        }
        else
            break;   

        //read digest num
        curIdx = partIdx + c_off_digestNum;
        UINT digestNum = 0;
        GetUintFromVector(vecByteDkp, curIdx, digestNum);
        //m_vecDigest.resize(digestNum);

        //1st digest
        curIdx += c_off_digest_0;
        UINT idxCurDigestStart = curIdx;
        for(UINT digestIdx = 0; digestIdx < digestNum+1; digestIdx++){
            //CDigest& entry = m_vecDigest[digestIdx];
            entry.Clear();
            entry.m_str = _T("");

            curIdx = idxCurDigestStart + c_off_digest_addr_start;
            ifok = GetUintFromVector(vecByteDkp, curIdx, entry.m_idxStart);

            curIdx = curIdx + c_off_digest_addr_end;
            ifok = GetUintFromVector(vecByteDkp, curIdx, entry.m_idxEnd);

            //add to vec
            m_vecDigest.push_back(entry);

            //one digest end
            //idxCurDigestStart += c_one_digest_len;
            ifok = FindVectorInVector(vecByteDkp, vecSingleDigestHead, curIdx, idxCurDigestStart);
            if(!ifok)
                break;
            //idxCurDigestStart = findIdx;
        }
    }

    return true;
}

bool GetUintFromVector(const vector<byte>& vec, UINT idxSrc, UINT& dest)
{
    byte temp0=0, temp1=0 , temp2=0, temp3 = 0;
    if(idxSrc+3 >= vec.size() )
        return false;

    UINT idx = idxSrc;
    temp0 = vec[idx++];
    temp1 = vec[idx++];
    temp2 = vec[idx++];
    temp3 = vec[idx++];
    dest = temp0 + temp1*0x100 + temp2*0x10000 + temp3 * 0x1000000;

    return true;
}

bool FindVectorInVector(const VEC_BYTE& vecSrc, const VEC_BYTE& vecToFind, UINT idxStart, UINT& idxFind)
{
    const UINT vecDestNum = (UINT)vecToFind.size();
    if(vecDestNum < 1)
        return false;

    UINT idxDest=0;
    for(UINT idxSrc= idxStart;  idxSrc +vecDestNum < vecSrc.size();  idxSrc ++){
        for(idxDest=0; idxDest < vecDestNum; idxDest++){
            if(vecSrc[idxSrc + idxDest] != vecToFind[idxDest]){
                break;
            }
        }
        if(idxDest >=vecDestNum){
            //find it
            idxFind = idxSrc;
            return true;
        }
    }
    return false;
}

void CDigest::Clear()
{
    m_idxStart = 0;
    m_idxEnd = 0;
    m_str = _T("");
}

bool    CDuokanDigestDlg::SaveToFile(const CString& cstrFileName)
{
    if(cstrFileName.GetLength() < 1 )
        return false;
    CFile file(cstrFileName, CFile::modeCreate|CFile::modeWrite);
    //CArchive ar(&file, CArchive::store);
    
    CString cstrEnd = _T("\n");
    string strEnd = UtilString::ConvertWideCharToMultiByte(cstrEnd.GetString() );
    //ar << cstrEnd;

    string str;
    for(UINT idx=0; idx< m_vecDigest.size(); idx++){
        const CDigest& entry = m_vecDigest[idx];
        if(entry.m_str.GetLength() <1)
            continue;
        str = UtilString::ConvertWideCharToMultiByte(entry.m_str.GetString() );
        file.Write(&str[0], (UINT)str.size() );

        file.Write(&strEnd[0], (UINT)strEnd.size() );
    }
    //ar.Close();
    file.Close();
    return true;
}


void CDuokanDigestDlg::OnBtnAbout()
{
    CDlgAbout dlg;
    dlg.DoModal();
}
